﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using NetTopologySuite.Algorithm;
using NetTopologySuite.Operation;
using NetTopologySuite.Utilities;

namespace NetTopologySuite.Geometries
{
	[Serializable]
	public class LineString : Geometry, ILineString, ICurve, IGeometry, ICloneable, IComparable, IComparable<IGeometry>, IEquatable<IGeometry>, ILineal
	{
		public override Coordinate[] Coordinates
		{
			get
			{
				return this._points.ToCoordinateArray();
			}
		}

		public override double[] GetOrdinates(Ordinate ordinate)
		{
			if (this.IsEmpty)
			{
				return new double[0];
			}
			Ordinates ordinates = OrdinatesUtility.ToOrdinatesFlag(new Ordinate[]
			{
				ordinate
			});
			if ((this._points.Ordinates & ordinates) != ordinates)
			{
				return Geometry.CreateArray(this._points.Count, double.NaN);
			}
			return Geometry.CreateArray(this._points, ordinate);
		}

		public ICoordinateSequence CoordinateSequence
		{
			get
			{
				return this._points;
			}
		}

		public Coordinate GetCoordinateN(int n)
		{
			return this._points.GetCoordinate(n);
		}

		public override Coordinate Coordinate
		{
			get
			{
				if (this.IsEmpty)
				{
					return null;
				}
				return this._points.GetCoordinate(0);
			}
		}

		public override Dimension Dimension
		{
			get
			{
				return Dimension.Curve;
			}
		}

		public override Dimension BoundaryDimension
		{
			get
			{
				if (this.IsClosed)
				{
					return Dimension.False;
				}
				return Dimension.Point;
			}
		}

		public override bool IsEmpty
		{
			get
			{
				return this._points.Count == 0;
			}
		}

		public override int NumPoints
		{
			get
			{
				return this._points.Count;
			}
		}

		public IPoint GetPointN(int n)
		{
			return base.Factory.CreatePoint(this._points.GetCoordinate(n));
		}

		public IPoint StartPoint
		{
			get
			{
				if (this.IsEmpty)
				{
					return null;
				}
				return this.GetPointN(0);
			}
		}

		public IPoint EndPoint
		{
			get
			{
				if (this.IsEmpty)
				{
					return null;
				}
				return this.GetPointN(this.NumPoints - 1);
			}
		}

		public virtual bool IsClosed
		{
			get
			{
				return !this.IsEmpty && this.GetCoordinateN(0).Equals2D(this.GetCoordinateN(this.NumPoints - 1));
			}
		}

		public bool IsRing
		{
			get
			{
				return this.IsClosed && base.IsSimple;
			}
		}

		public override string GeometryType
		{
			get
			{
				return "LineString";
			}
		}

		public override OgcGeometryType OgcGeometryType
		{
			get
			{
				return OgcGeometryType.LineString;
			}
		}

		public override double Length
		{
			get
			{
				return CGAlgorithms.Length(this._points);
			}
		}

		public override IGeometry Boundary
		{
			get
			{
				return new BoundaryOp(this).GetBoundary();
			}
		}

		public override IGeometry Reverse()
		{
			ICoordinateSequence coordinates = this._points.Reversed();
			return base.Factory.CreateLineString(coordinates);
		}

		public bool IsCoordinate(Coordinate pt)
		{
			for (int i = 0; i < this._points.Count; i++)
			{
				if (this._points.GetCoordinate(i).Equals(pt))
				{
					return true;
				}
			}
			return false;
		}

		protected override Envelope ComputeEnvelopeInternal()
		{
			if (this.IsEmpty)
			{
				return new Envelope();
			}
			Coordinate[] array = this._points.ToCoordinateArray();
			double num = array[0].X;
			double num2 = array[0].Y;
			double num3 = array[0].X;
			double num4 = array[0].Y;
			for (int i = 1; i < array.Length; i++)
			{
				num = ((num < array[i].X) ? num : array[i].X);
				num3 = ((num3 > array[i].X) ? num3 : array[i].X);
				num2 = ((num2 < array[i].Y) ? num2 : array[i].Y);
				num4 = ((num4 > array[i].Y) ? num4 : array[i].Y);
			}
			return new Envelope(num, num3, num2, num4);
		}

		public override bool EqualsExact(IGeometry other, double tolerance)
		{
			if (!this.IsEquivalentClass(other))
			{
				return false;
			}
			ILineString lineString = (ILineString)other;
			if (this._points.Count != lineString.NumPoints)
			{
				return false;
			}
			for (int i = 0; i < this._points.Count; i++)
			{
				if (!Geometry.Equal(this._points.GetCoordinate(i), lineString.GetCoordinateN(i), tolerance))
				{
					return false;
				}
			}
			return true;
		}

		public override void Apply(ICoordinateFilter filter)
		{
			for (int i = 0; i < this._points.Count; i++)
			{
				filter.Filter(this._points.GetCoordinate(i));
			}
		}

		public override void Apply(ICoordinateSequenceFilter filter)
		{
			if (this._points.Count == 0)
			{
				return;
			}
			for (int i = 0; i < this._points.Count; i++)
			{
				filter.Filter(this._points, i);
				if (filter.Done)
				{
					break;
				}
			}
			if (filter.GeometryChanged)
			{
				base.GeometryChanged();
			}
		}

		public override void Apply(IGeometryFilter filter)
		{
			filter.Filter(this);
		}

		public override void Apply(IGeometryComponentFilter filter)
		{
			filter.Filter(this);
		}

		public override object Clone()
		{
			LineString lineString = (LineString)base.Clone();
			lineString._points = (ICoordinateSequence)this._points.Clone();
			return lineString;
		}

		public override void Normalize()
		{
			for (int i = 0; i < this._points.Count / 2; i++)
			{
				int i2 = this._points.Count - 1 - i;
				if (!this._points.GetCoordinate(i).Equals(this._points.GetCoordinate(i2)))
				{
					if (this._points.GetCoordinate(i).CompareTo(this._points.GetCoordinate(i2)) > 0)
					{
						CoordinateSequences.Reverse(this._points);
					}
					return;
				}
			}
		}

		protected override bool IsEquivalentClass(IGeometry other)
		{
			return other is ILineString;
		}

		protected internal override int CompareToSameClass(object o)
		{
			Assert.IsTrue(o is ILineString);
			ILineString lineString = (ILineString)o;
			int num = 0;
			int num2 = 0;
			while (num < this._points.Count && num2 < lineString.CoordinateSequence.Count)
			{
				int num3 = this._points.GetCoordinate(num).CompareTo(lineString.CoordinateSequence.GetCoordinate(num2));
				if (num3 != 0)
				{
					return num3;
				}
				num++;
				num2++;
			}
			if (num < this._points.Count)
			{
				return 1;
			}
			if (num2 < lineString.CoordinateSequence.Count)
			{
				return -1;
			}
			return 0;
		}

		protected internal override int CompareToSameClass(object o, IComparer<ICoordinateSequence> comp)
		{
			Assert.IsTrue(o is ILineString);
			ILineString lineString = (LineString)o;
			return comp.Compare(this._points, lineString.CoordinateSequence);
		}

		public LineString(Coordinate[] points) : this(Geometry.DefaultFactory.CoordinateSequenceFactory.Create(points), Geometry.DefaultFactory)
		{
		}

		public LineString(ICoordinateSequence points, IGeometryFactory factory) : base(factory)
		{
			if (points == null)
			{
				points = factory.CoordinateSequenceFactory.Create(new Coordinate[0]);
			}
			if (points.Count == 1)
			{
				throw new ArgumentException("Invalid number of points in LineString (found " + points.Count + " - must be 0 or >= 2)");
			}
			this._points = points;
		}

		public Coordinate this[int n]
		{
			get
			{
				return this._points.GetCoordinate(n);
			}
			set
			{
				this._points.SetOrdinate(n, Ordinate.X, value.X);
				this._points.SetOrdinate(n, Ordinate.Y, value.Y);
				this._points.SetOrdinate(n, Ordinate.Z, value.Z);
			}
		}

		public int Count
		{
			get
			{
				return this._points.Count;
			}
		}

		[Obsolete("Use AngleUtility")]
		public double Angle
		{
			get
			{
				double num = this.EndPoint.X - this.StartPoint.X;
				double num2 = this.EndPoint.Y - this.StartPoint.Y;
				double num3 = System.Math.Sqrt(num * num + num2 * num2);
				double num4 = Math.Asin(System.Math.Abs(this.EndPoint.Y - this.StartPoint.Y) / num3) * 180.0 / System.Math.PI;
				if ((this.StartPoint.X < this.EndPoint.X && this.StartPoint.Y > this.EndPoint.Y) || (this.StartPoint.X > this.EndPoint.X && this.StartPoint.Y < this.EndPoint.Y))
				{
					num4 = 360.0 - num4;
				}
				return num4;
			}
		}

		public static readonly ILineString Empty = new GeometryFactory().CreateLineString(new Coordinate[0]);

		private ICoordinateSequence _points;
	}
}
